﻿namespace BM.Tools.WebControls.MForm.Controls.Helper.EventValidation
{
    using System.Collections.Generic;
    using System.Linq;

    using Items;

    /// <summary>
    /// This class is responsible for registering ids of controls that could be created on the client side.
    /// This functionality is needed, since ASP.NET does not accept creating controls on the client side - it is prohibited due to the event validation functionality.
    /// Only ways to avoid ASP.NET exception is either turning off the event validation functionality or registering all possible controls' ids that can appear on the client side.
    /// </summary>
    public class EventValidationHelper
    {
        private EventValidationTree tree;

        private int counter;

        /// <summary>
        /// Initializes a new instance of the <see cref="EventValidationHelper"/> class.
        /// </summary>
        /// <param name="root">
        /// The root control.
        /// </param>
        public EventValidationHelper(Root root)
        {
            Root = root;
            tree = new EventValidationTree(IdSeparator);
        }

        /// <summary>
        /// Gets Root.
        /// </summary>
        public Root Root
        {
            get; 
            private set;
        }

        private char IdSeparator
        {
            get
            {
                return Root.Page.IdSeparator;
            }
        }

        /// <summary>
        /// Registers ids of controls that may be added and support event validation.
        /// </summary>
        public void Register()
        {
            if (Root.Page.EnableEventValidation == false)
            {
                return;
            }

            var prefix = Root.UniqueID.Substring(0, Root.UniqueID.Length - Root.ID.Length);
            foreach (var item in Root.Items)
            {
                var nodeList = new EventValidationNodeList
                                   {
                                       Prefix = prefix
                                   };
                Register(item, nodeList);
            }

            ProcessEventValidationTree();            
        }

        private static void AddItemNode(OffspringItem item, EventValidationNodeList nodeListClone)
        {
            string name;
            if (item.ParentItem == null)
            {
                name = item.Name;
            }
            else
            {
                name = item.UniqueID.Substring(item.ParentItem.UniqueID.Length + 1);
                name = name.Substring(0, name.LastIndexOf(item.Page.IdSeparator));
            }

            nodeListClone.Add(new EventValidationNode
            {
                Name = name,
                End = item.Basis.MaxOccurs
            });
        }

        private void ProcessEventValidationTree()
        {
            foreach (var nodeList in tree)
            {
                ProcessEventValidationNodeList(nodeList);                
            }            
        }

        private void ProcessEventValidationNodeList(EventValidationNodeList list)
        {
            CreateNodeId(list, 0, list.Prefix);            
        }

        private void CreateNodeId(EventValidationNodeList list, int current, string currentId)
        {
            if (current == list.Count)
            {
                RegisterNodeId(currentId, list.Suffixes);
                return;
            }

            var currentNode = list[current];

            for (int i = 1; i <= currentNode.End; i++)
            {
                if (current == 0)
                {
                    CreateNodeId(list, current + 1, string.Concat(currentId, currentNode.Name, IdSeparator, i));
                } 
                else
                {
                    CreateNodeId(list, current + 1, string.Concat(currentId, IdSeparator, currentNode.Name, IdSeparator, i));                    
                }                
            }
        }

        private void RegisterNodeId(string id, List<string> suffixes)
        {
            foreach (var suffix in suffixes)
            {
                RegisterIdForClientValidation(string.Concat(id, suffix));
            }            
        }

        private void RegisterIdForClientValidation(string id)
        {
            counter++;
            if (counter > 10000)
            {
                throw new MFormEventValidationException("The possible number of controls supporting event validation on your page exceeds 10000. You can either examine the MaxOccurs property of your MForm components or you can turn off the 'enableEventValidation' feautre on your page.");
            }

            Root.Page.ClientScript.RegisterForEventValidation(id);
        }

        private void Register(OffspringItem item, EventValidationNodeList nodeList)
        {
            var offspringsOwner = item as OffspringsOwnerItem;
            if (offspringsOwner != null)
            {
                RegisterOwner(offspringsOwner, nodeList);
                return;
            }

            RegisterLeaf(item, nodeList);
        }

        private void RegisterLeaf(OffspringItem item, EventValidationNodeList nodeList)
        {
            AddItemNode(item, nodeList);
            RegisterEventHandlers(item, nodeList);
        }

        private void RegisterOwner(OffspringsOwnerItem item, EventValidationNodeList nodeList)
        {
            foreach (var childItem in item.ChildItems)
            {
                var nodeListClone = nodeList.Clone();
                AddItemNode(item, nodeListClone);
                Register(childItem, nodeListClone);
            }

            RegisterEventHandlers(item, nodeList);
        }        

        private void RegisterEventHandlers(OffspringItem item, EventValidationNodeList nodeList)
        {
            if (tree.Contains(nodeList.GetHashName(IdSeparator)))
            {
                return;
            }

            var eventHandlers = item.GetEventHandlers();
            if (eventHandlers.Count() <= 0)
            {
                return;
            }

            var suffixes = eventHandlers.Select(x => x.UniqueID.Substring(item.UniqueID.Length));
            nodeList.Suffixes = new List<string>(suffixes);
            tree.Add(nodeList);
        }        
    }
}
